﻿using Entity;
using Entity.BaseEntitys;
using EntityFrameWorkCore;
using FrameWorkCore.EFCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using Autofac.Extensions.DependencyInjection;
using FrameWorkCore;
using ShardingCore.Extensions.ShardingPageExtensions;
using EFCore.BulkExtensions;
using ShardingCore.Extensions;

namespace Repository.Repository
{
    public class Repository : IRepository
    {

    }


    public class Repository<TEntity, TPrimaryKey, TDbContext> : Repository, IRepository<TEntity, TPrimaryKey, TDbContext>,IRepository where TEntity : class, IEntity<TPrimaryKey> where TDbContext : BaseDbContext<TDbContext>
    {


        private readonly BaseDbContext<TDbContext> dbContext;
        private readonly DbSet<TEntity> dbSet;
        private readonly IUnitOfWork unitOfWork;

        public Repository(IServiceProvider serviceProvider ,IUnitOfWork _unitOfWork)//根据泛型 动态切换 注入的上下文
        {
            dbContext = (BaseDbContext<TDbContext>)serviceProvider.GetService(typeof(TDbContext));
            dbSet = dbContext.Set<TEntity>();
            unitOfWork = _unitOfWork;
        }


        private IQueryable<TEntity> ExecutingFilter()
            => unitOfWork.DisableFilter() ? dbSet.IgnoreQueryFilters() : dbSet;

        public async Task<ShardingPagedResult<TEntity>> QueryByPage(int pageIndex = 1, int pageSize = 10)
            => await ExecutingFilter().ToShardingPageAsync(pageIndex, pageSize);

        public void Delete(TPrimaryKey id)
          => dbSet.Remove(dbSet.FirstOrDefault(x => x.Id.Equals(id)));


        public void Delete(Expression<Func<TEntity, bool>> predicate)
        {
            var dels =  dbSet.Where(predicate);
            foreach (var del in dels)
                dbSet.Remove(del);
        }


        public TEntity FirstOrDefault(TPrimaryKey id)
            => ExecutingFilter().Where(x => x.Id.Equals(id)).FirstOrDefault();

        public TEntity FirstOrDefault(Expression<Func<TEntity, bool>> predicate)
            => ExecutingFilter().Where(predicate).FirstOrDefault();

        public TEntity Get(TPrimaryKey id)
            => ExecutingFilter().Where(x => x.Id.Equals(id)).First();


        public IQueryable<TEntity> GetAll()
            => ExecutingFilter().AsQueryable();
            //ExecutingFilter().AsQueryable();

        public List<TEntity> GetAllList()
            => ExecutingFilter().ToList();

        public List<TEntity> GetAllList(Expression<Func<TEntity, bool>> predicate)
            => ExecutingFilter().Where(predicate).ToList();


        public async ValueTask<List<TEntity>> GetAllListAsync()
            => await ExecutingFilter().ToListAsync();


        public async ValueTask<List<TEntity>> GetAllListAsync(Expression<Func<TEntity, bool>> predicate)
             => await ExecutingFilter().Where(predicate).ToListAsync();


        public TEntity Insert(TEntity entity)
            => dbSet.Add(entity).Entity;


        public TEntity Update(TEntity entity)
        {

            var Entity = dbSet.Attach(entity);
                Entity.State = EntityState.Modified;
                dbSet.Update(entity);
            //dbContext.SaveChanges();
           return Entity.Entity;
        }

        #region 批量操作


        public void BatchInsert(List<TEntity> entitys)
        {
            var dbContexts = dbContext.BulkShardingEnumerable(entitys);
            foreach (var dataSourceMap in dbContexts)
                foreach (var tailMap in dataSourceMap.Value)
                    tailMap.Key.BulkInsert(tailMap.Value.ToList());      
        }


        public async ValueTask BatchInsertAsync(List<TEntity> entitys)
        {
            var dbContexts = dbContext.BulkShardingEnumerable(entitys);
            foreach (var dataSourceMap in dbContexts)
                foreach (var tailMap in dataSourceMap.Value)
                   await tailMap.Key.BulkInsertAsync(tailMap.Value.ToList());
        }

        public void BatchUpdateAll(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, TEntity>> updateExpression)
        {
            var dbContexts = dbContext.BulkShardingExpression(predicate);
            foreach (var dataSourceMap in dbContexts)
                foreach (var tailMap in dataSourceMap.Value)
                    tailMap.Set<TEntity>().Where(predicate).BatchUpdate(updateExpression);
        }

        public void BatchUpdateColums(Expression<Func<TEntity, bool>> predicate, TEntity updateValue, List<string> updateColumns = null)
        {
            var dbContexts = dbContext.BulkShardingExpression(predicate);
            foreach (var dataSourceMap in dbContexts)
                foreach (var tailMap in dataSourceMap.Value)
                    tailMap.Set<TEntity>().Where(predicate).BatchUpdate(updateValue, updateColumns);
        }




        public void BatchDelete(Expression<Func<TEntity, bool>> predicate)
        {
            var dbContexts = dbContext.BulkShardingExpression(predicate);
            foreach (var dataSourceMap in dbContexts)
                foreach (var tailMap in dataSourceMap.Value)
                    tailMap.Set<TEntity>().Where(predicate).BatchDelete();
        }



        public async ValueTask BatchUpdateAllAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, TEntity>> updateExpression)
        {
            var dbContexts = dbContext.BulkShardingExpression(predicate);
            foreach (var dataSourceMap in dbContexts)
                foreach (var tailMap in dataSourceMap.Value)
                   await tailMap.Set<TEntity>().Where(predicate).BatchUpdateAsync(updateExpression);
        }

        public async ValueTask BatchUpdateColumsAsync(Expression<Func<TEntity, bool>> predicate, TEntity updateValue, List<string> updateColumns = null)
        {
            var dbContexts = dbContext.BulkShardingExpression(predicate);
            foreach (var dataSourceMap in dbContexts)
                foreach (var tailMap in dataSourceMap.Value)
                   await tailMap.Set<TEntity>().Where(predicate).BatchUpdateAsync(updateValue, updateColumns);
        }

        public async ValueTask BatchDeleteAsync(Expression<Func<TEntity, bool>> predicate)
        {
            var dbContexts = dbContext.BulkShardingExpression(predicate);
            foreach (var dataSourceMap in dbContexts)
                foreach (var tailMap in dataSourceMap.Value)
                   await tailMap.Set<TEntity>().Where(predicate).BatchDeleteAsync();
        }
        #endregion


        public BaseDbContext<TDbContext> GetDbContext()
          => dbContext;

        public async ValueTask<TEntity> GetAsync(TPrimaryKey id)
             => await ExecutingFilter().Where(x => x.Id.Equals(id)).FirstAsync();


        public async ValueTask<TEntity> FirstOrDefaultAsync(TPrimaryKey id)
             => await ExecutingFilter().Where(x => x.Id.Equals(id)).FirstOrDefaultAsync();


        public async ValueTask<TEntity> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> predicate)
            => await ExecutingFilter().Where(predicate).FirstOrDefaultAsync();

        public async ValueTask<TEntity> InsertAsync(TEntity entity)
            => (await dbSet.AddAsync(entity)).Entity;


        public async ValueTask<TEntity> UpdateAsync(TEntity entity)
            => await Task.Run(() => dbSet.Update(entity).Entity);


        public void Delete(TEntity entity)
            => dbSet.Remove(entity);



        public async ValueTask DeleteAsync(TEntity entity)
             => await Task.Run(() => dbSet.Remove(entity));


        public async ValueTask DeleteAsync(TPrimaryKey id)
            => await Task.Run(() => dbSet.RemoveRange(dbSet.Where(x => x.Id.Equals(id)).ToList()));


        public async ValueTask DeleteAsync(Expression<Func<TEntity, bool>> predicate)
            => await Task.Run(() => dbSet.RemoveRange(dbSet.Where(predicate).ToList()));

        public int Count()
            => ExecutingFilter().Count();


        public async ValueTask<int> CountAsync()
            => await ExecutingFilter().CountAsync();
      

        public int Count(Expression<Func<TEntity, bool>> predicate)
            => ExecutingFilter().Count(predicate);


        public async ValueTask<int> CountAsync(Expression<Func<TEntity, bool>> predicate)
            => await ExecutingFilter().CountAsync(predicate);


        public long LongCount()
            => ExecutingFilter().LongCount();
 

        public async ValueTask<long> LongCountAsync()
            => await ExecutingFilter().LongCountAsync();


        public long LongCount(Expression<Func<TEntity, bool>> predicate)
            => ExecutingFilter().LongCount(predicate);
   

        public async ValueTask<long> LongCountAsync(Expression<Func<TEntity, bool>> predicate)
            => await ExecutingFilter().LongCountAsync(predicate);

        public TEntity Single(Expression<Func<TEntity, bool>> predicate)
            => ExecutingFilter().Single(predicate);


        public async ValueTask<TEntity> SingleAsync(Expression<Func<TEntity, bool>> predicate)
            => await ExecutingFilter().SingleAsync(predicate);

    }
}
